import AESUtil from './AESUtil';
import EventPublisher from './EventPublisher';
import LocalStorageImpl from './Impl/LocalStorageImpl';
import MapStorageImpl from './Impl/MapStorageImpl';
import ObjectStorageImpl from './Impl/ObjectStorageImpl';
import SessionStorageImpl from './Impl/SessionStorageImpl';

import type { IStorage } from './types';

/**
 * 智能存储类，支持多种存储类型、可选加密、全局过期时间与 key 前缀
 *
 * @example
 *   // 使用示例
 *   const storage = new SmartStorage({
 *     encryptEnabled: true,
 *     globalExpirationTime: 3600, // 1小时过期
 *     prefixKey: 'app_',
 *     storageType: 'local'
 *   });
 *
 *   // 设置数据
 *   storage.set('user', { name: 'John' }, 1800);
 *
 *   // 获取数据
 *   const user = storage.get('user');
 *
 *   // 监听storageChange事件
 *   window.addEventListener('storageChange', event => {
 *     console.log('Storage changed:', event.detail);
 *     // 处理存储变化逻辑
 *   });
 */
class SmartStorage {
  private readonly storage: IStorage;
  private readonly prefixKey: string;
  private readonly globalExpirationTime: number;
  private readonly encryptEnabled: boolean;
  private readonly storageType: 'local' | 'map' | 'object' | 'session';

  /**
   * 构造函数，初始化存储类型、加密、过期时间、key 前缀等
   *
   * @param options 配置项
   *
   *   - customStorage: 自定义存储实现
   *   - encryptEnabled: 是否启用加密
   *   - globalExpirationTime: 全局过期时间（秒）
   *   - prefixKey: key 前缀
   *   - storageType: 存储类型
   *   - secretKey: 加密密钥
   */
  constructor(
    options: {
      customStorage?: IStorage;
      encryptEnabled?: boolean;
      globalExpirationTime?: number;
      prefixKey?: string;
      secretKey?: string;
      storageType?: 'local' | 'map' | 'object' | 'session';
    } = {}
  ) {
    this.storageType = options.storageType ?? 'local';
    this.prefixKey = options.prefixKey ?? '';
    this.globalExpirationTime = options.globalExpirationTime ?? 0;
    this.encryptEnabled = options.encryptEnabled ?? false;
    if (options.secretKey) {
      AESUtil.setSecretKey(options.secretKey);
    }

    let storageInstance: IStorage;

    if (options.customStorage) {
      storageInstance = options.customStorage;
    } else {
      switch (this.storageType) {
        case 'local':
          storageInstance = new LocalStorageImpl();
          break;
        case 'session':
          storageInstance = new SessionStorageImpl();
          break;
        case 'map':
          storageInstance = new MapStorageImpl();
          break;
        case 'object':
        default:
          storageInstance = new ObjectStorageImpl();
          break;
      }
    }

    this.storage = storageInstance;
  }

  /**
   * 设置存储项，支持可选过期时间和强制覆盖，支持加密
   *
   * @param key 键名
   * @param value 任意类型的值
   * @param expirationTime 过期时间（秒），优先级高于全局过期时间
   * @param force 是否强制覆盖已存在 key
   */
  set(key: string, value: unknown, expirationTime?: number, force: boolean = false): void {
    const fullKey = this.prefixKey + key;
    let success = false;
    if (!force && this.storage.keyExists(fullKey)) {
      console.warn(`Key "${key}" already exists. Use force=true to overwrite.`);
    } else {
      const itemExpirationTime = expirationTime ?? this.globalExpirationTime;
      const data = JSON.stringify({
        expires: itemExpirationTime ? Date.now() + itemExpirationTime * 1000 : null,
        value,
      });
      let encryptedData = data;
      // 若启用加密，则加密存储内容
      if (this.encryptEnabled) {
        encryptedData = AESUtil.encrypt(data);
      }
      this.storage.setItem(fullKey, encryptedData);
      success = true;
    }
    EventPublisher.dispatchEvent(
      new CustomEvent('storageChange', {
        detail: { action: 'set', key, success },
      })
    );
  }

  /**
   * 获取指定 key 的值，自动解密与过期校验，支持传入默认值
   *
   * @param key 键名
   * @param defaultValue 默认返回值（可选），当未获取到值时返回
   * @returns 存储的值，若不存在或已过期返回 null 或 defaultValue
   */
  get<T>(key: string, defaultValue?: T): T {
    const fullKey = this.prefixKey + key;
    const encryptedData = this.storage.getItem(fullKey);
    let success = encryptedData !== null;
    let value: T | null = null;
    if (success && encryptedData) {
      try {
        // 若启用加密，先解密
        const data = this.encryptEnabled ? AESUtil.decrypt(encryptedData) : encryptedData;
        const parsedData = JSON.parse(data) as { expires: number | null; value: T };
        if (parsedData.expires && parsedData.expires < Date.now()) {
          this.storage.removeItem(fullKey);
          success = false;
        } else {
          value = parsedData.value;
        }
      } catch (error) {
        console.error('Decryption failed or the data format is incorrect', error);
        success = false;
      }
    }
    EventPublisher.dispatchEvent(
      new CustomEvent('storageChange', {
        detail: { action: 'get', key, success, value },
      })
    );
    if (value === null && typeof defaultValue !== 'undefined') {
      return defaultValue as T;
    }
    return value as T;
  }

  /**
   * 移除指定 key 的存储项
   *
   * @param key 键名
   */
  remove(key: string): void {
    const fullKey = this.prefixKey + key;
    const exists = this.storage.keyExists(fullKey);
    this.storage.removeItem(fullKey);
    const success = exists;
    EventPublisher.dispatchEvent(
      new CustomEvent('storageChange', {
        detail: { action: 'remove', key, success },
      })
    );
  }

  /** 清空所有存储内容 */
  clear(): void {
    this.storage.clear();
    EventPublisher.dispatchEvent(
      new CustomEvent('storageChange', {
        detail: { action: 'clear', success: true },
      })
    );
  }
}

export { AESUtil, EventPublisher, SmartStorage };
